﻿using System;

namespace CardGameCore.Common
{
    [Serializable]
    public struct Vector2Int : IEquatable<Vector2Int>
    {
        public Vector2Int(int x, int y)
        {
            X = x;
            Y = y;
        }

        public static Vector2Int Zero { get; } = new Vector2Int(0, 0);
        public static Vector2Int One { get; } = new Vector2Int(1, 1);
        public static Vector2Int Up { get; } = new Vector2Int(0, 1);
        public static Vector2Int Down { get; } = new Vector2Int(0, -1);
        public static Vector2Int Left { get; } = new Vector2Int(-1, 0);
        public static Vector2Int Right { get; } = new Vector2Int(1, 0);
        public static Vector2Int RightUp { get; } = new Vector2Int(1, 1);
        public static Vector2Int RightDown { get; } = new Vector2Int(1, -1);
        public static Vector2Int LeftUp { get; } = new Vector2Int(-1, 1);
        public static Vector2Int LeftDown { get; } = new Vector2Int(-1, -1);

        public static Vector2Int[] Offsets = {Up, RightUp, Right, RightDown, Down, LeftDown, Left, LeftUp};

        public int X;
        public int Y;

        public void Set(int x, int y)
        {
            X = x;
            Y = y;
        }

        public int this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return X;
                    case 1:
                        return Y;
                    default:
                        throw new IndexOutOfRangeException($"Invalid Vector2Int index addressed: {index}!");
                }
            }
            set
            {
                if (index != 0)
                {
                    if (index != 1)
                    {
                        throw new IndexOutOfRangeException($"Invalid Vector2Int index addressed: {index}!");
                    }

                    Y = value;
                }
                else
                {
                    X = value;
                }
            }
        }

        public double Magnitude => Math.Sqrt(SqrtMagnitude);

        public int SqrtMagnitude => X * X + Y * Y;

        public int RectArea => X * Y;

        public static Vector2Int Direction(Vector2Int a, Vector2Int b) => (a - b);

        public static double Distance(Vector2Int a, Vector2Int b) => (a - b).Magnitude;

        public static Vector2Int Min(Vector2Int lhs, Vector2Int rhs)
        {
            return new Vector2Int(Math.Min(lhs.X, rhs.X), Math.Min(lhs.Y, rhs.Y));
        }

        public static Vector2Int Max(Vector2Int lhs, Vector2Int rhs)
        {
            return new Vector2Int(Math.Max(lhs.X, rhs.X), Math.Max(lhs.Y, rhs.Y));
        }

        public static Vector2Int operator +(Vector2Int a, Vector2Int b)
        {
            return new Vector2Int(a.X + b.X, a.Y + b.Y);
        }

        public static Vector2Int operator -(Vector2Int a, Vector2Int b)
        {
            return new Vector2Int(a.X - b.X, a.Y - b.Y);
        }

        public static Vector2Int operator *(Vector2Int a, Vector2Int b)
        {
            return new Vector2Int(a.X * b.X, a.Y * b.Y);
        }

        public static Vector2Int operator *(Vector2Int a, int b)
        {
            return new Vector2Int(a.X * b, a.Y * b);
        }

        public static bool operator ==(Vector2Int lhs, Vector2Int rhs)
        {
            return lhs.X == rhs.X && lhs.Y == rhs.Y;
        }

        public static bool operator !=(Vector2Int lhs, Vector2Int rhs)
        {
            return !(lhs == rhs);
        }

        public override int GetHashCode() => X ^ Y << 16;

        public override bool Equals(object obj)
        {
            return obj is Vector2Int other && Equals(other);
        }

        public bool Equals(Vector2Int other)
        {
            return X.Equals(other.X) && Y.Equals(other.Y);
        }

        public override string ToString() => $"({X} {Y})";
    }
}